package main

import (
    "fmt"
    "reflect"
)

type Student struct {
	Name 	string `json:"name"`
	Age  	int    `json:"age"`
	Score int    `json:"score"`
}

func (s Student) GetInfo() string {
	return fmt.Sprintf("name:%s, age:%d, score:%d", s.Name, s.Age, s.Score)
}

func (s * Student) Constructor(name string, age int, score int) {
	s.Name = name
	s.Age = age
	s.Score = score
}

func (s *Student) SetName(name string) {
	s.Name = name
}

func structFuncTest(x interface{}) {
	// 首先判断传入的是否为一个结构体，这里要考虑传入的是否为指针
	t := reflect.TypeOf(x)
	if t.Kind() != reflect.Struct && t.Elem().Kind() != reflect.Struct {
		fmt.Println("x is not a struct")
		return
	}

	// 1、通过类型变量中的Method获取结构体的方法
	m0 := t.Method(0)		// 这里第0个和结构体定义方法的顺序没有关系，和结构体方法的ASCII码值有关（按照方法名的ASCII码值排序）
	fmt.Printf("%#v\n", m0)
	// reflect.Method{Name:"GetInfo", PkgPath:"", Type:(*reflect.rtype)(0xc0000620a0), Func:reflect.Value{typ_:(*abi.Type)(0xc0000620a0), ptr:(unsafe.Pointer)(0xc000050090), flag:0x13}, Index:0}
	// 通过方法名获取结构体的方法
	m1, ok := t.MethodByName("GetInfo")
	if (!ok) {
		fmt.Println("method not found")
	} else {
		fmt.Printf("%#v\n", m1)
		// reflect.Method{Name:"GetInfo", PkgPath:"", Type:(*reflect.rtype)(0xc0000620a0), Func:reflect.Value{typ_:(*abi.Type)(0xc0000620a0), ptr:(unsafe.Pointer)(0xc000050098), flag:0x13}, Index:0}
	}

	// 2、通过【值变量】执行方法（注意需要使用值变量，并且要注意参数），v.Method(0).Call(nil)或者v.MethodByName("GetInfo").Call(nil)	
	v := reflect.ValueOf(x)
	// fmt.Println(v.Method(0).Call(nil))
	// [name:Tom, age:18, score:99]
	fmt.Println(v.MethodByName("GetInfo").Call(nil))
	// [name:Tom, age:18, score:99]

	// 3、调用方法，传入参数（注意使用值变量，注意参数，接收的参数是[]reflect.Value的切片）
	// 首先定义类型为reflect.Value的切片
	var params []reflect.Value
	params = append(params, reflect.ValueOf("Jerry"))
	params = append(params, reflect.ValueOf(20))
	params = append(params, reflect.ValueOf(88))
	v.MethodByName("Constructor").Call(params)
	// 再次获取值
	fmt.Println(v.MethodByName("GetInfo").Call(nil))

	// 4、获取方法的数量
	var methodNum = t.NumMethod()
	fmt.Println("方法的数量为：", methodNum)
}

func main() {
	// var a = 10
	// structFuncTest(a)

	var s = &Student{Name: "Tom", Age: 18, Score: 99}
	structFuncTest(s)
}



